/*
 *  Copyright 2008-2022, 2024 NXP
 *
 *  SPDX-License-Identifier: BSD-3-Clause
 *
 */

/*!\file wm_utils.h
 *\brief This file provides utility functions for Wi-Fi connection manager.
 *
 * Utility functions
 */

#ifndef _UTIL_H_
#define _UTIL_H_

#include <wifi_config_default.h>
#include <wmtypes.h>
#include <stddef.h>
#include <stdint.h>
#include <ctype.h>
#ifdef __ZEPHYR__
#include <zephyr/kernel.h>
#include <strings.h>
#else
#include "fsl_debug_console.h"
#endif

#if CONFIG_WIFI_SMOKE_TESTS
#if defined(SDK_OS_FREE_RTOS)

#undef PRINTF
extern void sm_printf(const char *fmt, ...);

#define PRINTF sm_printf
#elif __ZEPHYR__
#endif
#endif

#ifdef __ZEPHYR__
#ifndef PRINTF
#define PRINTF printk
#endif
#ifndef SDK_DEBUGCONSOLE
#define SDK_DEBUGCONSOLE CONFIG_WIFI_EXTRA_DEBUG
#endif
#endif

#define ffs __builtin_ffs

#ifdef __GNUC__
#define WARN_UNUSED_RET __attribute__((warn_unused_result))

#ifndef PACK_START
#define PACK_START
#endif
#ifndef PACK_END
#define PACK_END __attribute__((packed))
#endif
#define NORETURN __attribute__((noreturn))

/* alignment value should be a power of 2 */
#define ALIGN_X(num, align) WM_MASK(num, (typeof(num))align - 1)

#define ALIGN_2(num)  ALIGN_X(num, 2)
#define ALIGN_4(num)  ALIGN_X(num, 4)
#define ALIGN_8(num)  ALIGN_X(num, 8)
#define ALIGN_16(num) ALIGN_X(num, 16)
#define ALIGN_32(num) ALIGN_X(num, 32)

#else /* __GNUC__ */

#define WARN_UNUSED_RET

#define PACK_START __packed
#define PACK_END
#define NORETURN

#endif /* __GNUC__ */

/* Weak function. */
#if defined(__GNUC__)
#define WEAK __attribute__((weak))
#elif defined(__ICCARM__)
#define WEAK __weak
#elif defined(__CC_ARM) || defined(__ARMCC_VERSION)
#define WEAK __attribute__((weak))
#endif

/* alignment value should be a power of 2 */
#define __WM_ALIGN__(num, num_type, align) WM_MASK(num, (num_type)align - 1)
#define WM_MASK(num, mask)                 ((num + mask) & ~(mask))

NORETURN void wmpanic(void);

/**
 * Convert a given hex string to a equivalent binary representation.
 *
 * E.g. If your input string of 4 bytes is {'F', 'F', 'F', 'F'} the output
 * string will be of 2 bytes {255, 255} or to put the same in other way
 * {0xFF, 0xFF}
 *
 * Note that hex2bin is not the same as strtoul as the latter will properly
 * return the integer in the correct machine binary format viz. little
 * endian. hex2bin however does only in-place like replacement of two ASCII
 * characters to one binary number taking 1 byte in memory.
 *
 * @param[in] ibuf input buffer
 * @param[out] obuf output buffer
 * @param[in]  max_olen Maximum output buffer length
 *
 * @return length of the binary string
 */
static inline unsigned int wm_hex2bin(const uint8_t *ibuf, uint8_t *obuf, unsigned max_olen)
{
#ifndef __ZEPHYR__
    unsigned int i;      /* loop iteration variable */
    unsigned int j  = 0; /* current character */
    unsigned int by = 0; /* byte value for conversion */
    unsigned char ch;    /* current character */
    unsigned int len = strlen((const char *)ibuf);
    /* process the list of characters */
    for (i = 0; i < len; i++)
    {
        if (i == (2U * max_olen))
        {
            (void)PRINTF("hexbin",
                         "Destination full. "
                         "Truncating to avoid overflow.\r\n");
            return j + 1U;
        }
        ch = (unsigned char)toupper(*ibuf++); /* get next uppercase character */

        /* do the conversion */
        if (ch >= '0' && ch <= '9')
        {
            by = (by << 4) + ch - '0';
        }
        else if (ch >= 'A' && ch <= 'F')
        {
            by = (by << 4) + ch - 'A' + 10U;
        }
        else
        { /* error if not hexadecimal */
            return 0;
        }

        /* store a byte for each pair of hexadecimal digits */
        if ((i & 1) == 1U)
        {
            j       = ((i + 1U) / 2U) - 1U;
            obuf[j] = (uint8_t)(by & 0xffU);
        }
    }
    return j + 1U;
#else
    return hex2bin(ibuf, strlen(ibuf), obuf, max_olen);
#endif
}

#ifndef __ZEPHYR__
/**
 * Convert given binary array to equivalent hex representation.
 *
 * @param[in] src Input buffer
 * @param[out]  dest Output buffer
 * @param[in] src_len Length of the input buffer
 * @param[in] dest_len Length of the output buffer
 *
 */
void wm_bin2hex(uint8_t *src, char *dest, unsigned int src_len, unsigned int dest_len);
#endif /* ! __ZEPHYR__ */

/** Function prototype for a random entropy/seed generator
 *
 * \return a 32bit random number
 */
typedef uint32_t (*random_hdlr_t)(void);

/** Register a random entropy generator handler
 *
 * This API allows applications to register their own random entropy
 * generator handlers that will be internally used by get_random_sequence()
 * to add even more randomization to the byte stream generated by it.
 *
 * \param[in] func Function pointer of type \ref random_hdlr_t
 *
 * \return WM_SUCCESS if successful
 * \return -WM_E_NOSPC if there is no space available for additional handlers
 */
int random_register_handler(random_hdlr_t func);

/** Un-register a random entropy generator handler
 *
 * This API can be used to un-register a handler registered using
 * random_register_handler()
 *
 * \param[in] func Function pointer of type \ref random_hdlr_t used during
 * registering
 *
 * \return WM_SUCCESS if successful
 * \return -WM_E_INVAL if the passed pointer is invalid
 */
int random_unregister_handler(random_hdlr_t func);

/** Register a random seed generator handler
 *
 * For getting better random numbers, the initial seed (ideally required only
 * once on every boot) should also be random. This API allows applications to
 * register their own seed generators. Applications can use any logic such
 * that a different seed is generated every time. A sample seed generator which
 * uses a combination of DAC (generating random noise) and ADC (that internally
 * samples the random noise) along with the flash id has already been provided.
 * Please have a look at sample_initialise_random_seed().
 *
 * The seed generator handler is called only once by the get_random_sequence()
 * function. Applications can also explicitly initialize the seed by calling
 * random_initialize_seed() after registering a handler.
 *
 * \param[in] func Function pointer of type \ref random_hdlr_t
 *
 * \return WM_SUCCESS if successful
 * \return -WM_E_NOSPC if there is no space available for additional handlers
 */
int random_register_seed_handler(random_hdlr_t func);

/** Un-register a random seed generator handler
 *
 * This API can be used to un-register a handler registered using
 * random_register_seed_handler()
 *
 * \param[in] func Function pointer of type \ref random_hdlr_t used during
 * registering
 *
 * \return WM_SUCCESS if successful
 * \return -WM_E_INVAL if the passed pointer is invalid
 */
int random_unregister_seed_handler(random_hdlr_t func);

/** Initialize the random number generator's seed
 *
 * The get_random_sequence() uses a random number generator that is
 * initialized with a seed when get_random_sequence() is called for the first
 * time. The handlers registered using random_register_seed_handler() are
 * used to generate the seed. If an application wants to explicitly initialize
 * the seed, this API can be used. The seed will then not be re-initialized
 * in get_random_sequence().
 */
void random_initialize_seed(void);

/** Sample random seed generator
 *
 * This is a sample random seed generator handler that can be registered using
 * random_register_seed_handler() to generate a random seed. This uses a
 * combination of DAC (generating random noise) and ADC (that internally
 * samples the random noise) along with the flash id to generate a seed.
 * It is recommended to register this handler and immediately call
 * random_initialize_seed() before executing any other application code,
 * especially if the application is going to use ADC/DAC for its own purpose.
 *
 * \return Random seed
 */
uint32_t sample_initialise_random_seed(void);

/** Generate random sequence of bytes
 *
 * This function generates random sequence of bytes in the user provided buffer.
 *
 * @param[out] buf The buffer to be populated with random data
 * @param[in] size The number of bytes of the random sequence required
 *
 */
void get_random_sequence(void *buf, unsigned int size);

#if (SDK_DEBUGCONSOLE != DEBUGCONSOLE_DISABLE) || defined(__ZEPHYR__)
#define DUMP_WRAPAROUND 16U

/** Dump buffer in hex format on console
 *
 * This function prints the received buffer in HEX format on the console
 *
 * \param[in] data Pointer to the data buffer
 * \param[in] len Length of the data
 */
static inline void dump_hex(const void *data, unsigned len)
{
    (void)PRINTF("**** Dump @ %p Len: %d ****\n\r", data, len);

    unsigned int i    = 0;
    const char *data8 = (const char *)data;
    while (i < len)
    {
        (void)PRINTF("%02x ", data8[i++]);
        if (!(i % DUMP_WRAPAROUND))
        {
            (void)PRINTF("\n\r");
        }
    }

    (void)PRINTF("\n\r******** End Dump *******\n\r");
}
/** Dump buffer in hex and ascii format on console
 *
 * This function prints the received buffer in HEX as well as ASCII
 * format on the console
 *
 * \param[in] data Pointer to the data buffer
 * \param[in] len Length of the data
 */
void dump_hex_ascii(const void *data, unsigned len);
void dump_ascii(const void *data, unsigned len);
void print_ascii(const void *data, unsigned len);
void dump_json(const void *buffer, unsigned len);
#else
#define dump_hex(...) \
    do                \
    {                 \
    } while (0)
#define dump_hex_ascii(...) \
    do                      \
    {                       \
    } while (0)
#define dump_ascii(...) \
    do                  \
    {                   \
    } while (0)
#define print_ascii(...) \
    do                   \
    {                    \
    } while (0)
#define dump_json(...) \
    do                 \
    {                  \
    } while (0)
#endif

/* Helper functions to print a float value. Some compilers have a problem
 * interpreting %f
 */

#define wm_int_part_of(x) ((int)(x))
static inline int wm_frac_part_of(float x, short precision)
{
    int scale = 1;

    while ((precision--) != (short)0U)
    {
        scale *= 10;
    }

    return (x < 0 ? (int)(((int)x - x) * scale) : (int)((x - (int)x) * scale));
}

#if CONFIG_SIGMA_AGENT
#if defined(SDK_OS_FREE_RTOS)
#if (defined(__MCUXPRESSO) || defined(__GNUC__)) && !defined(__ARMCC_VERSION)
static inline int strcasecmp(const char *a, const char *b)
{
    int ca, cb;
    do
    {
        ca = *(unsigned char *)a;
        cb = *(unsigned char *)b;
        ca = tolower(toupper(ca));
        cb = tolower(toupper(cb));
        a++;
        b++;
    } while (ca == cb && ca != '\0');
    return ca - cb;
}
#endif
#endif
#endif

#ifndef __linux__
/** Returns a pointer to a new string which is a duplicate of the
 *  input string s.
 *  Memory for the new string is obtained allocated by the function.
 *
 *  It is caller's responsibility to free the memory after its use.
 *
 *  \param[in] s Pointer to string to be duplicated
 *
 *  \return Pointer to newly allocated string which is duplicate of
 *  input string
 *  \return NULL on error
 */
char *strdup(const char *s);
#endif /* ! __linux__ */

/** Calculate CRC32 using software algorithm
 *
 * @pre soft_crc32_init()
 *
 * soft_crc32() allows the user to calculate CRC32 values of arbitrary
 * sized buffers across multiple calls.
 *
 * @param[in] data__ Input buffer over which CRC32 is calculated.
 * @param[in] data_size Length of the input buffer.
 * @param[in] crc Previous CRC32 value used as starting point for given
 * buffer calculation.
 *
 * @return Calculated CRC32 value
 */
uint32_t soft_crc32(const void *data__, int data_size, uint32_t crc);
float wm_strtof(const char *str, char **endptr);

/**
 * Fill the given buffer with a sequential pattern starting from
 * given byte.
 *
 * For example, if the 'first_byte' is 0x45 and buffer size of 5 then
 * buffer will be set to {0x45, 0x46, 0x47, 0x48, 0x49}
 * @param[in] buffer The pattern will be set to this buffer.
 * @param[in] size Number of pattern bytes to the be written to the buffer.
 * @param[in] first_byte This is the value of first byte in the sequential
 * pattern.
 *
 */
void fill_sequential_pattern(void *buffer, int size, uint8_t first_byte);

/**
 * Verify if the the given buffer has a sequential pattern starting from
 * given byte.
 *
 * For example, if the 'first_byte' is 0x45 and buffer size of 5 then
 * buffer will be verified for presence of {0x45, 0x46, 0x47, 0x48, 0x49}
 * @param[in] buffer The pattern will be verified from this buffer.
 * @param[in] size Number of pattern bytes to the be verified from the buffer.
 * @param[in] first_byte This is the value of first byte in the sequential
 * pattern.
 *
 * @return 'true' If verification successful.
 * @return 'false' If verification fails.
 */
bool verify_sequential_pattern(const void *buffer, int size, uint8_t first_byte);
#endif
